<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JSON 与 XML 格式转换工具</title>
    <link rel="stylesheet" href="styles.css">
    <link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.7.2/css/all.min.css" rel="stylesheet">
    <script type="text/javascript" charset="UTF-8" src="https://cdn.wwads.cn/js/makemoney.js" async></script>
</head>
<body>
    <header>
        <div class="header-container">
            <div class="header-left">
                <div class="logo">
                    <span><a href="https://geekdaxue.co/" target="_blank"><img src="https://geekdaxue.co/static/images/logo_platypus.png" height="30px"></a></span>
                </div>
                <nav class="main-navigation">
                    <div class="nav-item dropdown">
                        <a href="#" class="nav-link">
                            JSON工具 <i class="fas fa-chevron-down"></i>
                        </a>
                        <div class="dropdown-menu">
                            <a href="../json/index.html">JSON 格式化</a>
                            <a href="../json/parser.html">JSON 格式校验</a>
                            <a href="../json/excel.html">JSON 转 Excel/CSV</a>
                            <a href="../json/jsonxml.html">JSON 与 XML 转换</a>
                            <a href="../json/editor.html">JSON 编辑器</a>
                            <a href="../json/txt2json_arr.html">文本转JSON数组</a>
                        </div>
                    </div>
                    <div class="nav-item dropdown">
                        <a href="#" class="nav-link">
                            编码转换 <i class="fas fa-chevron-down"></i>
                        </a>
                        <div class="dropdown-menu">
                            <a href="../coding/base64.html">Base64 编码</a>
                            <a href="../coding/urlencoding.html">URL 编码</a>
                            <a href="../coding/md5.html">MD5 加密</a>
                            <a href="../coding/unicode.html">Unicode 转换</a>
                            <a href="../coding/hex_convert.html">进制转换</a>
                        </div>
                    </div>
                    <div class="nav-item dropdown">
                        <a href="#" class="nav-link">
                            文本工具 <i class="fas fa-chevron-down"></i>
                        </a>
                        <div class="dropdown-menu">
                            <a href="../txt/diff.html">文本对比</a>
                            <a href="../txt/replace.html">文本替换</a>
                            <a href="../txt/remove.html">空白行移除</a>
                            <a href="../txt/cn_trans.html">繁简体转换</a>
                        </div>
                    </div>
                    <div class="nav-item dropdown">
                        <a href="#" class="nav-link">
                            图片工具 <i class="fas fa-chevron-down"></i>
                        </a>
                        <div class="dropdown-menu">
                            <a href="../img/2svg.html">图片转 SVG</a>
                            <a href="../img/qr_code.html">二维码生成</a>
                            <a href="../img/barcode.html">条形码生成</a>
                        </div>
                    </div>
                </nav>
            </div>
            <div class="header-controls">
                <div class="theme-toggle">
                    <input type="checkbox" id="theme-switch" class="theme-switch">
                    <label for="theme-switch" class="theme-switch-label">
                        <i class="fas fa-sun"></i>
                        <i class="fas fa-moon"></i>
                        <span class="slider"></span>
                    </label>
                </div>
                <div class="share-button" id="share-button" title="分享给朋友">
                    <i class="fas fa-share-alt"></i>
                    <span>分享</span>
                </div>
            </div>
        </div>
    </header>
    <div class="container">
        <h1>JSON/XML 互相转换</h1>
        <div id="privacy-notice" class="privacy-notice">
            <div class="privacy-notice-content">
                <i class="fas fa-shield-alt"></i>
                <p>安全提示：您的数据仅在本地浏览器中处理，不会上传到服务器，请放心使用！</p>
                <button id="close-notice" class="close-notice" title="关闭提示">
                    <i class="fas fa-times"></i>
                </button>
            </div>
        </div>
        
        <main>
            <div class="editor-container">
                <div class="input-section">
                    <h2>输入数据</h2>
                    <textarea id="input-data" placeholder="在此粘贴您的 JSON 或 XML 数据..."></textarea>
                    <div class="button-group">
                        <button id="json-to-xml-btn">JSON 转 XML</button>
                        <button id="xml-to-json-btn">XML 转 JSON</button>
                        <button id="clear-btn">清空</button>
                    </div>
                    <div id="validation-result" class="validation-result"></div>
                </div>
                
                <div class="output-section">
                    <div class="output-header">
                        <h2>转换结果</h2>
                        <div class="output-buttons">
                            <button id="copy-btn" class="copy-btn" title="复制到剪贴板">
                                <i class="fas fa-copy"></i> 复制
                            </button>
                        </div>
                    </div>
                    <pre id="output-data"></pre>
                </div>
            </div>
        </main>
        <div class="wwads-cn wwads-horizontal" data-id="321" style="max-width:350px"></div>
    </div>
    <footer>
            <div class="footer-container">
                <div class="footer-main">
                    <div class="footer-section about-us">
                        <h3>关于我们</h3>
                        <p>我们提供一系列高效、便捷的在线工具，帮助开发者、非技术人员提高工作效率，解决日常开发中遇到的各种问题。</p>
                        <p>Build With <i class="fa-solid fa-heart" style="color: red;"></i> For SF.</p>
                    </div>
                    <div class="footer-section links">
                        <h3>工具导航</h3>
                        <div class="links-columns">
                            <ul>
                                <li><a href="../json/index.html">JSON 工具</a></li>
                                <li><a href="../img/index.html">图片处理</a></li>
                                <li><a href="#">开发工具</a></li>
                            </ul>
                            <ul>
                                <li><a href="../coding/index.html">编码转换</a></li>
                                <li><a href="../txt/index.html">文本工具</a></li>
                            </ul>
                        </div>
                    </div>
                    <div class="footer-section contact">
                        <h3>联系我们</h3>
                        <div class="social-icons">
                            <a href="#" title="GitHub"><i class="fab fa-github"></i></a>
                            <a href="#" title="Twitter"><i class="fab fa-twitter"></i></a>
                            <a href="#" title="WeChat"><i class="fab fa-weixin"></i></a>
                            <a href="mailto:example@example.com" title="Email"><i class="fas fa-envelope"></i></a>
                        </div>
                        <p>如有问题或建议，请随时联系我们</p>
                    </div>
                </div>
                <div class="footer-bottom">
                    <p>© 2025 JSON 格式化工具 Pro. 保留所有权利</p>
                    <div class="footer-legal">
                        <a href="#">使用条款</a>
                        <a href="#">隐私政策</a>
                    </div>
                </div>
            </div>
        </footer>
    
    <div id="copy-notification" class="copy-notification">已复制到剪贴板！</div>
    <div id="share-notification" class="copy-notification">已复制网址，赶快让更多的小伙伴知道我吧！</div>
    
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // 主题切换功能
            const themeSwitch = document.getElementById('theme-switch');
            const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
            
            // 检查本地存储中的主题设置或系统偏好
            if (localStorage.getItem('theme') === 'dark' || (!localStorage.getItem('theme') && prefersDarkScheme.matches)) {
                document.body.setAttribute('data-theme', 'dark');
                themeSwitch.checked = true;
            }
            
            themeSwitch.addEventListener('change', function() {
                if (this.checked) {
                    document.body.setAttribute('data-theme', 'dark');
                    localStorage.setItem('theme', 'dark');
                } else {
                    document.body.removeAttribute('data-theme');
                    localStorage.setItem('theme', 'light');
                }
            });
            
            // 隐私提示关闭功能
            const closeNoticeBtn = document.getElementById('close-notice');
            const privacyNotice = document.getElementById('privacy-notice');
            
            closeNoticeBtn.addEventListener('click', function() {
                privacyNotice.style.display = 'none';
                localStorage.setItem('privacy-notice-closed', 'true');
            });
            
            // 检查是否已经关闭过隐私提示
            if (localStorage.getItem('privacy-notice-closed') === 'true') {
                privacyNotice.style.display = 'none';
            }
            
            // 分享功能
            const shareButton = document.getElementById('share-button');
            const shareNotification = document.getElementById('share-notification');
            
            shareButton.addEventListener('click', function() {
                navigator.clipboard.writeText(window.location.href).then(function() {
                    shareNotification.classList.add('show');
                    setTimeout(function() {
                        shareNotification.classList.remove('show');
                    }, 2000);
                });
            });
            
            // 转换功能
            const inputData = document.getElementById('input-data');
            const outputData = document.getElementById('output-data');
            const jsonToXmlBtn = document.getElementById('json-to-xml-btn');
            const xmlToJsonBtn = document.getElementById('xml-to-json-btn');
            const clearBtn = document.getElementById('clear-btn');
            const validationResult = document.getElementById('validation-result');
            const copyBtn = document.getElementById('copy-btn');
            const copyNotification = document.getElementById('copy-notification');
            
            // JSON 转 XML
            jsonToXmlBtn.addEventListener('click', function() {
                const jsonStr = inputData.value.trim();
                if (!jsonStr) {
                    showValidationError('请输入 JSON 数据');
                    return;
                }
                
                try {
                    // 尝试解析 JSON
                    const jsonObj = JSON.parse(jsonStr);
                    
                    // 转换为 XML
                    const xmlStr = jsonToXml(jsonObj);
                    
                    // 显示转换结果
                    outputData.textContent = xmlStr;
                    
                    // 转换成功
                    showValidationSuccess('JSON 转 XML 成功');
                } catch (error) {
                    // 解析错误
                    showJsonError(jsonStr, error);
                }
            });
            
            // XML 转 JSON
            xmlToJsonBtn.addEventListener('click', function() {
                const xmlStr = inputData.value.trim();
                if (!xmlStr) {
                    showValidationError('请输入 XML 数据');
                    return;
                }
                
                try {
                    // 尝试解析 XML
                    const parser = new DOMParser();
                    const xmlDoc = parser.parseFromString(xmlStr, 'text/xml');
                    
                    // 检查解析错误
                    const parserError = xmlDoc.querySelector('parsererror');
                    if (parserError) {
                        throw new Error(parserError.textContent);
                    }
                    
                    // 转换为 JSON
                    const jsonObj = xmlToJson(xmlDoc);
                    
                    // 显示转换结果
                    outputData.textContent = JSON.stringify(jsonObj, null, 4);
                    
                    // 转换成功
                    showValidationSuccess('XML 转 JSON 成功');
                } catch (error) {
                    // 解析错误
                    showXmlError(xmlStr, error);
                }
            });
            
            // 清空输入
            clearBtn.addEventListener('click', function() {
                inputData.value = '';
                outputData.textContent = '';
                validationResult.innerHTML = '';
                validationResult.classList.remove('show');
            });
            
            // 复制到剪贴板
            copyBtn.addEventListener('click', function() {
                const textToCopy = outputData.textContent;
                if (!textToCopy) return;
                
                navigator.clipboard.writeText(textToCopy).then(function() {
                    copyNotification.classList.add('show');
                    setTimeout(function() {
                        copyNotification.classList.remove('show');
                    }, 2000);
                });
            });
            
            // 显示 JSON 校验错误
            function showJsonError(jsonStr, error) {
                const errorMessage = error.message;
                const positionMatch = errorMessage.match(/position (\d+)/);
                
                if (positionMatch) {
                    const position = parseInt(positionMatch[1]);
                    const lines = jsonStr.substring(0, position).split('\n');
                    const lineNumber = lines.length;
                    const columnNumber = lines[lines.length - 1].length + 1;
                    
                    // 获取错误行的内容
                    const allLines = jsonStr.split('\n');
                    const errorLine = allLines[lineNumber - 1];
                    
                    // 构建错误信息
                    let errorHTML = `<span class="error">JSON 格式错误：第 ${lineNumber} 行，第 ${columnNumber} 列</span><br>`;
                    errorHTML += `<pre class="error-line">${errorLine.replace(/ /g, '&nbsp;')}</pre>`;
                    errorHTML += `<pre class="error-pointer">${'&nbsp;'.repeat(columnNumber - 1)}^</pre>`;
                    errorHTML += `<span class="error-detail">${error.message}</span>`;
                    
                    validationResult.innerHTML = errorHTML;
                } else {
                    validationResult.innerHTML = `<span class="error">JSON 格式错误：${error.message}</span>`;
                }
                
                validationResult.classList.add('show');
            }
            
            // 显示 XML 校验错误
            function showXmlError(xmlStr, error) {
                validationResult.innerHTML = `<span class="error">XML 格式错误：${error.message}</span>`;
                validationResult.classList.add('show');
            }
            
            // 显示校验错误
            function showValidationError(message) {
                validationResult.innerHTML = `<span class="error">${message}</span>`;
                validationResult.classList.add('show');
            }
            
            // 显示校验成功
            function showValidationSuccess(message) {
                validationResult.innerHTML = `<span class="success">${message}</span>`;
                validationResult.classList.add('show');
            }
            
            // JSON 转 XML 函数
            function jsonToXml(obj) {
                let xml = '';
                
                // 处理根节点
                if (typeof obj === 'object' && Object.keys(obj).length === 1) {
                    const rootKey = Object.keys(obj)[0];
                    xml = `<?xml version="1.0" encoding="UTF-8"?>\n<${rootKey}>${processJsonNode(obj[rootKey], 1)}</${rootKey}>`;
                } else {
                    xml = `<?xml version="1.0" encoding="UTF-8"?>\n<root>${processJsonNode(obj, 1)}</root>`;
                }
                
                return formatXml(xml);
            }
            
            // 处理 JSON 节点
            function processJsonNode(node, level) {
                let xml = '';
                const indent = '    '.repeat(level);
                
                if (node === null || node === undefined) {
                    return '';
                } else if (typeof node === 'object') {
                    if (Array.isArray(node)) {
                        // 处理数组
                        for (let i = 0; i < node.length; i++) {
                            xml += `\n${indent}<item>${processJsonNode(node[i], level + 1)}</item>`;
                        }
                    } else {
                        // 处理对象
                        for (const key in node) {
                            if (node.hasOwnProperty(key)) {
                                const value = node[key];
                                if (value === null || value === undefined) {
                                    xml += `\n${indent}<${key}/>`;
                                } else if (typeof value === 'object') {
                                    xml += `\n${indent}<${key}>${processJsonNode(value, level + 1)}\n${indent}</${key}>`;
                                } else {
                                    xml += `\n${indent}<${key}>${escapeXml(value.toString())}</${key}>`;
                                }
                            }
                        }
                    }
                } else {
                    // 处理基本类型
                    xml += escapeXml(node.toString());
                }
                
                return xml;
            }
            
            // XML 转 JSON 函数
            function xmlToJson(xml) {
                // 创建空对象，用于存储结果
                let obj = {};
                
                // 如果节点没有子节点，则为文本节点
                if (xml.nodeType === 3) {
                    return xml.nodeValue.trim();
                }
                
                // 如果有属性，添加到结果对象
                if (xml.attributes && xml.attributes.length > 0) {
                    for (let i = 0; i < xml.attributes.length; i++) {
                        const attribute = xml.attributes[i];
                        obj[`@${attribute.nodeName}`] = attribute.nodeValue;
                    }
                }
                
                // 处理子节点
                for (let i = 0; i < xml.childNodes.length; i++) {
                    const item = xml.childNodes[i];
                    const nodeName = item.nodeName;
                    
                    if (nodeName === '#text') {
                        if (item.nodeValue.trim() !== '') {
                            if (Object.keys(obj).length === 0) {
                                return item.nodeValue.trim();
                            } else {
                                obj['#text'] = item.nodeValue.trim();
                            }
                        }
                    } else if (nodeName !== '#comment') {
                        const itemObj = xmlToJson(item);
                        
                        if (obj[nodeName] === undefined) {
                            obj[nodeName] = itemObj;
                        } else {
                            if (!Array.isArray(obj[nodeName])) {
                                obj[nodeName] = [obj[nodeName]];
                            }
                            obj[nodeName].push(itemObj);
                        }
                    }
                }
                
                return obj;
            }
            
            // 格式化 XML
            function formatXml(xml) {
                let formatted = '';
                let indent = '';
                
                xml.split('\n').forEach(function(node) {
                    if (node.match(/.+<\/\w[^>]*>$/)) {
                        // 完整节点行
                        formatted += indent + node + '\n';
                    } else if (node.match(/^<\/\w/)) {
                        // 结束节点行
                        indent = indent.substring(4);
                        formatted += indent + node + '\n';
                    } else if (node.match(/^<\w[^>]*[^/]>.*$/)) {
                        // 开始节点行
                        formatted += indent + node + '\n';
                        indent += '    ';
                    } else {
                        // 声明、自闭合节点等
                        formatted += indent + node + '\n';
                    }
                });
                
                return formatted.trim();
            }
            
            // 转义 XML 特殊字符
            function escapeXml(unsafe) {
                return unsafe
                    .replace(/&/g, '&amp;')
                    .replace(/</g, '&lt;')
                    .replace(/>/g, '&gt;')
                    .replace(/"/g, '&quot;')
                    .replace(/'/g, '&apos;');
            }
        });
    </script>
    
    <style>
        /* 校验结果样式 */
        .validation-result {
            margin-top: 15px;
            padding: 10px;
            border-radius: 4px;
            font-size: 14px;
            display: none;
        }
        
        .validation-result.show {
            display: block;
        }
        
        .validation-result .success {
            color: var(--success-color);
            font-weight: bold;
        }
        
        .validation-result .error {
            color: var(--error-color);
            font-weight: bold;
        }
        
        .error-line {
            background-color: rgba(231, 76, 60, 0.1);
            padding: 5px;
            margin: 5px 0;
            border-radius: 3px;
            font-family: monospace;
            white-space: pre-wrap;
            overflow-x: auto;
        }
        
        .error-pointer {
            color: var(--error-color);
            font-family: monospace;
            margin: 0;
            font-weight: bold;
        }
        
        .error-detail {
            font-size: 12px;
            color: var(--text-color);
            opacity: 0.8;
            margin-top: 5px;
            display: block;
        }
        
        /* 输入输出区域样式 */
        #input-data, #output-data {
            flex: 1;
            width: 100%;
            padding: 12px;
            border: 1px solid var(--border-color);
            border-radius: 4px;
            font-family: 'Consolas', monospace;
            font-size: 14px;
            background-color: var(--card-background);
            color: var(--text-color);
            transition: border-color 0.3s, background-color 0.3s, color 0.3s;
        }
        
        #input-data {
            resize: none;
            height: 500px;
        }
        
        #input-data:focus {
            outline: none;
            border-color: var(--primary-color);
        }
        
        #output-data {
            overflow: auto;
            white-space: pre-wrap;
            height: 500px;
        }
    </style>
</body>
</html>